///
// * jdbm LICENSE v1.00
// *
// * Redistribution and use of this software and associated documentation
// * ("Software"), with or without modification, are permitted provided
// * that the following conditions are met:
// *
// * 1. Redistributions of source code must retain copyright
// *    statements and notices.  Redistributions must also contain a
// *    copy of this document.
// *
// * 2. Redistributions in binary form must reproduce the
// *    above copyright notice, this list of conditions and the
// *    following disclaimer in the documentation and/or other
// *    materials provided with the distribution.
// *
// * 3. The name "jdbm" must not be used to endorse or promote
// *    products derived from this Software without prior written
// *    permission of Cees de Groot.  For written permission,
// *    please contact cg@cdegroot.com.
// *
// * 4. Products derived from this Software may not be called "jdbm"
// *    nor may "jdbm" appear in their names without prior written
// *    permission of Cees de Groot.
// *
// * 5. Due credit should be given to the jdbm Project
// *    (http://jdbm.sourceforge.net/).
// *
// * THIS SOFTWARE IS PROVIDED BY THE ndbm PROJECT AND CONTRIBUTORS
// * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
// * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
// * CEES DE GROOT OR ANY CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// * OF THE POSSIBILITY OF SUCH DAMAGE.
// *
// * Copyright 2000 (C) Cees de Groot. All Rights Reserved.
// * Contributions are Copyright (C) 2000 by their associated contributors.
// *
// * $Id: BlockIo.java,v 1.2 2002/08/06 05:18:36 boisvert Exp $
// 

//*************************************************************************
//**  Included in JDMB 1.0 port to SharpDBM;  11/2013 Cyrus Neah cneah@codingreal.com
//**  SharpDBM is an independent reimplementation of the JDBM 1.0 software library in C#  
//*************************************************************************

using System.Runtime.CompilerServices;
using System;
using System.Data;
using System.IO;
using System.Linq;
using SharpDBM.helper;

namespace SharpDBM.recman
{


///
// *  This class wraps a page-sized byte array and provides methods
// *  to read and write data to and from it. The readers and writers
// *  are just the ones that the rest of the toolkit needs, nothing else.

// *

// 
	[Serializable]
	public   class BlockIo 
	{

		public const long serialVersionUID = 2L;

		private long blockId;

	//	[System.NonSerialized]
		private sbyte[] data; // work area
		[System.NonSerialized]
		private BlockView view = null;
		[System.NonSerialized]
		private bool dirty = false;
		[System.NonSerialized]
		private int transactionCount = 0;

//    *
//     * Default constructor for serialization
//     
		public BlockIo()
		{
		// empty
		}

//    *
//     *  Constructs a new BlockIo instance working on the indicated
//     *  buffer.
//     
		public BlockIo(long blockId, sbyte[] data)
		{
		// removeme for production version
			if (blockId > 10000000000L)
				throw new Exception("bogus block id " + blockId);
			this.blockId = blockId;
			this.data = data;
		}

//    *
//     *  Returns the underlying array
//     
		internal virtual sbyte[] getData()
		{
			return data;
		}

        //set the underlying array
        internal virtual void setData(sbyte[] _data)
        {
            this.data=_data;
        }


//    *
//     *  Sets the block number. Should only be called by RecordFile.
//     
		internal virtual void setBlockId(long id)
		{
			if (isInTransaction())
				throw new Exception("BlockId assigned for transaction block");
		// removeme for production version
			if (id > 10000000000L)
				throw new Exception("bogus block id " + id);
			blockId = id;
		}

//    *
//     *  Returns the block number.
//     
		internal virtual long getBlockId()
		{
			return blockId;
		}

//    *
//     *  Returns the current view of the block.
//     
		public virtual BlockView getView()
		{
			return view;
		}

//    *
//     *  Sets the current view of the block.
//     
		public virtual void setView(BlockView view)
		{
			this.view = view;
		}

//    *
//     *  Sets the dirty flag
//     
		internal virtual void setDirty()
		{
			dirty = true;
		}

//    *
//     *  Clears the dirty flag
//     
		internal virtual void setClean()
		{
			dirty = false;
		}

//    *
//     *  Returns true if the dirty flag is set.
//     
		internal virtual bool isDirty()
		{
			return dirty;
		}

//    *
//     *  Returns true if the block is still dirty with respect to the
//     *  transaction log.
//     
		internal virtual bool isInTransaction()
		{
			return transactionCount != 0;
		}

//    *
//     *  Increments transaction count for this block, to signal that this
//     *  block is in the log but not yet in the data file. The method also
//     *  takes a snapshot so that the data may be modified in new transactions.
//     
		[MethodImpl(MethodImplOptions.Synchronized)]
		internal virtual void incrementTransactionCount()
		{
			transactionCount++;
		// @fixme(alex)
			setClean();
		}

//    *
//     *  Decrements transaction count for this block, to signal that this
//     *  block has been written from the log to the data file.
//     
		[MethodImpl(MethodImplOptions.Synchronized)]
		internal virtual void decrementTransactionCount()
		{
			transactionCount--;
			if (transactionCount < 0)
				throw new Error("transaction count on block " + getBlockId() + " below zero!");

		}

//    *
//     *  Reads a byte from the indicated position
//     
		public virtual sbyte readByte(int pos)
		{
			return data[pos];
		}

//    *
//     *  Writes a byte to the indicated position
//     
		public virtual void writeByte(int pos, sbyte @value)
		{
			data[pos] = @value;
			setDirty();
		}

//    *
//     *  Reads a short from the indicated position
//     
		public virtual short readShort(int pos)
		{
			return (short)(((short)(data[pos+0] & 0xff) << 8) | ((short)(data[pos+1] & 0xff) << 0));
		}

//    *
//     *  Writes a short to the indicated position
//     
		public virtual void writeShort(int pos, short @value)
		{
			data[pos+0] = (sbyte)(0xff & (@value >> 8));
			data[pos+1] = (sbyte)(0xff & (@value >> 0));
			setDirty();
		}

//    *
//     *  Reads an int from the indicated position
//     
		public virtual int readInt(int pos)
		{
			return (((int)(data[pos+0] & 0xff) << 24) | ((int)(data[pos+1] & 0xff) << 16) | ((int)(data[pos+2] & 0xff) << 8) | ((int)(data[pos+3] & 0xff) << 0));
		}

//    *
//     *  Writes an int to the indicated position
//     
		public virtual void writeInt(int pos, int @value)
		{
			data[pos+0] = (sbyte)(0xff & (@value >> 24));
			data[pos+1] = (sbyte)(0xff & (@value >> 16));
			data[pos+2] = (sbyte)(0xff & (@value >> 8));
			data[pos+3] = (sbyte)(0xff & (@value >> 0));
			setDirty();
		}

//    *
//     *  Reads a long from the indicated position
//     
		public virtual long readLong(int pos)
		{
		// Contributed by Erwin Bolwidt <ejb@klomp.org>
		// Gives about 15% performance improvement

            //sbyte[] sbytes = { data[pos + 0], data[pos + 1],
            //data[pos + 2],data[pos + 3],data[pos + 4],data[pos + 5]
            //,data[pos + 6],data[pos + 7]};
            

           // if (BitConverter.IsLittleEndian) Array.Reverse(sbytes);
           // byte[] bytes = Array.ConvertAll(sbytes, b => (byte)b);
           // return (long)BitConverter.ToInt64(bytes, 0);
            return
                 ((long)(((data[pos + 0] & 0xff) << 24) |
                      ((data[pos + 1] & 0xff) << 16) |
                      ((data[pos + 2] & 0xff) << 8) |
                      ((data[pos + 3] & 0xff))) << 32) |
            ((long)(((data[pos + 4] & 0xff) << 24) |
                      ((data[pos + 5] & 0xff) << 16) |
                      ((data[pos + 6] & 0xff) << 8) |
                      ((data[pos + 7] & 0xff))) & 0xffffffff);
//         Original version by Alex Boisvert. Might be faster on 64-bit JVMs.
//        return
//            (((long)(data[pos+0] & 0xff) << 56) |
//             ((long)(data[pos+1] & 0xff) << 48) |
//             ((long)(data[pos+2] & 0xff) << 40) |
//             ((long)(data[pos+3] & 0xff) << 32) |
//             ((long)(data[pos+4] & 0xff) << 24) |
//             ((long)(data[pos+5] & 0xff) << 16) |
//             ((long)(data[pos+6] & 0xff) <<  8) |
//             ((long)(data[pos+7] & 0xff) <<  0));
//        
		}

//    *
//     *  Writes a long to the indicated position
//     
		public virtual void writeLong(int pos, long @value)
		{
			data[pos+0] = (sbyte)(0xff & (@value >> 56));
			data[pos+1] = (sbyte)(0xff & (@value >> 48));
			data[pos+2] = (sbyte)(0xff & (@value >> 40));
			data[pos+3] = (sbyte)(0xff & (@value >> 32));
			data[pos+4] = (sbyte)(0xff & (@value >> 24));
			data[pos+5] = (sbyte)(0xff & (@value >> 16));
			data[pos+6] = (sbyte)(0xff & (@value >> 8));
			data[pos+7] = (sbyte)(0xff & (@value >> 0));
			setDirty();
		}

	// overrides java.lang.Object

		public override string ToString()
		{
			return "BlockIO(" + blockId + "," + dirty + "," + view + ")";
		}

	// implement externalizable interface
		public virtual void readExternal(BinaryReader @in)
		{
			blockId = @in.ReadInt64();
			int length = @in.ReadInt32();
			data = new sbyte[length];
			byte[] unsigned = Array.ConvertAll(data, b => (byte)b);
			@in.Read(unsigned, 0, unsigned.Length);
		}

	// implement externalizable interface

		public virtual void writeExternal(BinaryWriter @out)
		{
			@out.Write(blockId);
			@out.Write(data.Length);
			byte[] unsigned = Array.ConvertAll(data, b => (byte)b);
			@out.Write(unsigned, 0, unsigned.Length);
		}

	}

}